﻿using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Collections.Immutable;

namespace ConsoleApp
{
    // 集合（Collection）类是专门用于数据存储和检索的类。
    // 数组（Array, Bit Array） 
    // 列表（List, Sort List, LinkedList（链接列表）) : 频繁地插入和删除元素比 List<T> 更加高效, List:适用于需要快速访问和遍历的有序集合
    // 字典（Dictionary, Sort Dictionary(SortedDictionary<TKey, TValue>)） : 根据键来快速查找或存储值
    // 集合（Set, Sort Set(SortedSet<T>), HashSet<T>） : 快速查找元素
    // 队列（Queue） : 用于特定的 FIFO 和 LIFO 操作
    // 栈（Stack） : 用于特定的 FIFO 和 LIFO 操作
    // 哈希表（hash table）
    internal class DemoCollection
    {
        // 一、选择合适的数据结构和存储方式; 
        // 使用 Span 和 Memory 优化数组操作: 对于高性能场景，Span<T> 和 Memory<T> 提供了更轻量级的内存管理和数据操作
        
        // 1. 对于动态增长的集合，如 List<T> 和 Dictionary<TKey, TValue>，未指定容量时，集合会动态扩容，增加额外的内存分配和复制操作。
        // 解决方法: 如果可以预估集合的大小，尽量通过构造函数设置初始容量。
        // 对于大型集合用 IEnumerable<T>代替List<T>; 延迟加载Lazy<T>
        // 2. 避免装箱和拆箱
        // 使用非泛型集合（如 ArrayList、Hashtable）时，值类型会发生装箱和拆箱操作，影响性能。
        // 解决方法: 使用泛型集合（如 List<T>、Dictionary<TKey, TValue>）来避免装箱和拆箱。
        
        // 使用 HashSet<T> 或 Dictionary<TKey, TValue> 优化查找 避免多次对同一个集合进行操作。例如，在遍历集合时，不要嵌套对集合的频繁查找 (`Contains` 是线性操作)
        
        // 二、优化数据处理算法(); 
        // 使用高效的遍历方式
        // for 循环: 对于 List<T>，for 循环通常比 foreach 更快，尤其是当需要通过索引访问元素时。
        // foreach 循环: 更适合遍历不可变集合（如数组）或无法通过索引访问的集合。
        
        // 数据分区优化(并行/分块): 使用 Partitioner 或分块操作处理大数据集合。
        
        // 三、内存管理和性能优化: 对象复用(对象池), 垃圾回收控制
        
        
        //操作	示例
        //声明	string[] arr = { "A", "B" };
        //遍历	foreach (var item in arr) { ... }
        //访问	arr[0]
        //查找	Array.IndexOf(arr, "A")
        //排序	Array.Sort(arr)
        //连接	string.Join(", ", arr)
        //转 List	List<string> list = arr.ToList()
        //过滤	arr.Where(x => x.StartsWith("A")).ToArray()
        
        // 动态数组（ArrayList）
        public void ArrayListTest()
        {
            // 声明并初始化
            var al = new ArrayList
            {
                1,
                3,
                2
            };

            Console.WriteLine("Capacity: {0} ", al.Capacity);
            Console.WriteLine("Count: {0}", al.Count);

            // 遍历
            Console.Write("Content: ");
            foreach (int i in al)
            {
                Console.Write(i + " ");
            }
            Console.WriteLine();

            // 排序
            Console.Write("Sorted Content: ");
            al.Sort();
            foreach (int i in al)
            {
                Console.Write(i + " ");
            }
            Console.WriteLine();
            Console.ReadKey();
        }

        // 哈希表（Hashtable）
        public void HashtableTest()
        {
            // 直接定义一个哈希表
            //Hashtable aa = new Hashtable
            //{
            //    {"键1", 1},
            //    {"键2", 1},
            //    {"键3", 1}
            //};

            // 声明
            var ht = new Hashtable
            {
                { 1, "刘备" },
                { 2, "关羽" },
                { 3, "张飞" },
                { 4, "赵云" }
            };

            // 查重
            Console.WriteLine(" " + ht.Contains(1));

            // 修改
            ht[4] = "刘禅";

            // 移除
            //ht.Remove(4);

            // 清空
            //ht.Clear();

            //遍历方法一：遍历哈希表中的键
            foreach (string key in ht.Keys)
            {
                Console.WriteLine(ht[key]);
            }
            Console.WriteLine("--------------------");

            //遍历方法二：遍历哈希表中的值
            foreach (string value in ht.Values)
            {
                Console.WriteLine(value);
            }
            Console.WriteLine("--------------------");

            //遍历方法三：遍历哈希表中的键值
            foreach (DictionaryEntry c in ht)
            {
                //string? k = c.Key as string; 
                //string? v = c.Value as string;
                Console.Write(c.Key + " " + c.Value + "\n");
            }
            Console.WriteLine("--------------------");

            //遍历方法四：遍历哈希表中的键值
            var myEnumerator = ht.GetEnumerator();
            while (myEnumerator.MoveNext())
            {
                Console.WriteLine(ht[myEnumerator.Key]);
            }

            Console.WriteLine();
            Console.ReadKey();

            // 使用情景
            //(1) 某些数据会被高频率查询
            //(2) 数据量大
            //(3) 查询字段包含字符串类型
            //(4) 数据类型不唯一
        }

        // 列表（List）
        // List 是个强类型，很安全。其次看那个尖括号，它是 C#2.0 时加入的泛型，所以并不存在像 ArrayList。那样要拆 / 装箱以此造成性能浪费。
        public void ListTest()
        {
            // 原始列表
            var numbers = new List<int> { 1, 2, 3, 4, 5 };
            var addValue = 10; // 要加的数
            Console.WriteLine("原始列表: " + string.Join(", ", numbers));
        
            // 性能由低到高
            // 方法1: 使用for循环
            for (var i = 0; i < numbers.Count; i++)
            {
                numbers[i] += addValue;
            }
            Console.WriteLine("方法1(for循环): " + string.Join(", ", numbers));
        
            // 重置列表
            numbers = [1, 2, 3, 4, 5];
        
            // 方法2: 使用Select创建新列表
            var newNumbers = numbers.Select(x => x + addValue).ToList();
            Console.WriteLine("方法2(Select创建新列表): " + string.Join(", ", newNumbers));
        
            // 方法3: 使用ConvertAll
            var converted = numbers.ConvertAll(x => x + addValue);
            Console.WriteLine("方法3(ConvertAll): " + string.Join(", ", converted));
            
            
            // 声明 集合初始化器
            var list = new List<int>
            {
                1,
                3,
                2
            };

            // 读取 List 通过索引分配，索引与数组一样，从 0 开始。它可以通过索引来读取值
            Console.WriteLine(list[0]);

            // 删除 Remove() 删掉项中第一个匹配你想删除的条件的项（删去第一个匹配此条件的项）
            //list.Remove(3);

            // Clear() 清空所有项
            //list.Clear();

            // Sort() 用系统默认的方式对项进行排序
            list.Sort();

            // Contains() 查看某项是否存在于列表中
            Console.Write(" " + list.Contains(1));

            //list.AddRange(new int[] { 1, 2, 3, 4, 5, 6 });
            //list.AddRange(list);

            //List泛型集合可以转换为数组
            //int[] nums = list.ToArray();

            //List<string> listStr = new List<string>();
            //string[] str = listStr.ToArray();


            //char[] chs = new char[] { 'c', 'b', 'a' };
            //List<char> listChar = chs.ToList();
            //for (int i = 0; i < listChar.Count; i++)
            //{
            //    Console.WriteLine(listChar[i]);
            //}

            ////   List<int> listTwo = nums.ToList();

            // 遍历列表
            for (int i = 0; i < list.Count; i++)
            {
                Console.WriteLine(list[i]);
            }
            Console.ReadKey();
        }

        // 排序列表（SortedList）
        public void SortedListTest()
        {
            var sl = new SortedList
            {
                { "001", "Zara Ali" },
                { "002", "Abida Rehman" },
                { "003", "Joe Holzner" },
                { "004", "Mausam Benazir Nur" },
                { "005", "M. Amlan" },
                { "006", "M. Arif" },
                { "007", "Ritesh Saikia" }
            };

            if (sl.ContainsValue("Nuha Ali"))
            {
                Console.WriteLine("This student name is already in the list");
            }
            else
            {
                sl.Add("008", "Nuha Ali");
            }

            // 获取键的集合
            var key = sl.Keys;

            foreach (string k in key)
            {
                Console.WriteLine(k + ": " + sl[k]);
            }
        }

        // 字典（Dictionary）
        public void DictionaryTest()
        {
            var dic = new Dictionary<string, int>
            {
                { "apple", 1 },
                { "banana", 2 },
                { "orange", 3 }
            };
            
            // 声明
            var dict = new Dictionary<int, int>();

            // 添加
            dict.Add(1, 1);
            dict.Add(2, 2);
            dict.Add(3, 3);


            // 查重
            dict.ContainsKey(1);
            dict.ContainsValue(2);

            // 修改
            dict[3] = 4;

            // 数量
            Console.WriteLine(dict.Count);

            // 移除
            dict.Remove(3);

            // 清空
            dict.Clear();

            // 遍历字典
            foreach (var kv in dict)
            {
                Console.WriteLine("{0}---{1}", kv.Key, kv.Value);
            }

            foreach (var d in dict)
            {
                Console.Write(d.Key);
            }
            
            // 判断value有无空
            var dictionary = new Dictionary<string, string>
            {
                { "key1", "value1" },
                { "key2", "" }, // 空字符串
                { "key3", null } // null值
            };
 
            // 方法1：使用LINQ
            var hasNullOrEmptyValue1 = dictionary.Any(kvp => string.IsNullOrEmpty(kvp.Value));
            Console.WriteLine(hasNullOrEmptyValue1); // 输出: True
            
            // 方法2：遍历字典
            var hasNullOrEmptyValue2 = false;
            foreach (var kvp in dictionary)
            {
                if (!string.IsNullOrEmpty(kvp.Value)) continue;
                hasNullOrEmptyValue2 = true;
                break; // 如果只需要知道是否存在，找到第一个即可退出循环
            }
            Console.WriteLine(hasNullOrEmptyValue2); // 输出: True
            
        }

        // LinkedList<T>是一种双向链表，允许在任意位置高效地添加和移除元素
        public void LinkedListTest()
        {
            var linkedList = new LinkedList<int>(); // 创建一个整数链表
            linkedList.AddLast(1); // 在链表末尾添加一个元素
            linkedList.AddFirst(0); // 在链表开头添加一个元素
            linkedList.RemoveLast();
            linkedList.RemoveFirst();
        }

        // 堆栈（Stack）： 后进先出
        public void StackTest()
        {
            var st = new Stack();
            // Count
            // Clear
            // Contains
            // Peek
            // Pop
            
            // Push
            
            st.Push('A');
            st.Push('M');
            st.Push('G');
            st.Push('W');

            Console.WriteLine("Current stack: ");
            foreach (char c in st)
            {
                Console.Write(c + " ");
            }
            Console.WriteLine();

            st.Push('V');
            st.Push('H');
            Console.WriteLine("The next poppable value in stack: {0}",
            st.Peek());
            Console.WriteLine("Current stack: ");
            foreach (char c in st)
            {
                Console.Write(c + " ");
            }
            Console.WriteLine();

            Console.WriteLine("Removing values ");
            st.Pop();
            st.Pop();
            st.Pop();

            Console.WriteLine("Current stack: ");
            foreach (char c in st)
            {
                Console.Write(c + " ");
            }
        }

        // 队列（Queue） : 先进先出 关于排队进行的业务
        public void QueueTest()
        {
            Queue q = new Queue();//线程不安全的队列, 非并发的情况

            //入队(Enqueue)
            //出队(TryDequeue)
            //出队不删除队列中的元素TryPeek
            //是否为空(IsEmpty)
            //获取队列内元素数量(Count)
            //清空并复位(Reset)

            q.Enqueue('A');
            q.Enqueue('M');
            q.Enqueue('G');
            q.Enqueue('W');

            Console.WriteLine("Current queue: ");
            foreach (char c in q)
                Console.Write(c + " ");
            Console.WriteLine();
            q.Enqueue('V');
            q.Enqueue('H');
            Console.WriteLine("Current queue: ");
            foreach (char c in q)
                Console.Write(c + " ");
            Console.WriteLine();
            Console.WriteLine("Removing some values ");
            char ch = (char)q.Dequeue();
            Console.WriteLine("The removed value: {0}", ch);
            ch = (char)q.Dequeue();
            Console.WriteLine("The removed value: {0}", ch);
            
            // 应用：比如接口日志、邮件、短信异步处理
            var cq = new ConcurrentQueue<int>();//线程安全的队列 并发
            cq.Enqueue(1);
            cq.Enqueue(2);
            
            var count = cq.Count;
            for (var i = 0; i < count; i++)
            {
                var source = cq.TryDequeue(out var entity);
                if (source)
                {
                    Console.WriteLine("队列出队元素：" + entity);
                }
            }

            Console.ReadKey();
        }
        
        // 点阵列（BitArray）
        public void BitArrayTest()
        {
            // 创建两个大小为 8 的点阵列
            var ba1 = new BitArray(8);
            var ba2 = new BitArray(8);
            byte[] a = { 60 };
            byte[] b = { 13 };

            // 把值 60 和 13 存储到点阵列中
            ba1 = new BitArray(a);
            ba2 = new BitArray(b);

            // ba1 的内容
            Console.WriteLine("Bit array ba1: 60");
            for (int i = 0; i < ba1.Count; i++)
            {
                Console.Write("{0, -6} ", ba1[i]);
            }
            Console.WriteLine();

            // ba2 的内容
            Console.WriteLine("Bit array ba2: 13");
            for (int i = 0; i < ba2.Count; i++)
            {
                Console.Write("{0, -6} ", ba2[i]);
            }
            Console.WriteLine();


            var ba3 = new BitArray(8);
            ba3 = ba1.And(ba2);

            // ba3 的内容
            Console.WriteLine("Bit array ba3 after AND operation: 12");
            for (int i = 0; i < ba3.Count; i++)
            {
                Console.Write("{0, -6} ", ba3[i]);
            }
            Console.WriteLine();

            ba3 = ba1.Or(ba2);
            // ba3 的内容
            Console.WriteLine("Bit array ba3 after OR operation: 61");
            for (int i = 0; i < ba3.Count; i++)
            {
                Console.Write("{0, -6} ", ba3[i]);
            }
            Console.WriteLine();

            Console.ReadKey();
        }


        // 字典,堆栈，队列不能排序，如果想对字典排序就要用其它方法或集合，如SortedDictionary<TKey,TValue>
        
        // 哈希表与字典的区别
        // 1.字典元素的顺序就是插入的顺序，而哈希表则不一定。
        // 2.字典是强类型的，哈希表是弱类型的。
        public void DemoFrozenCollection()
        {
            // Immutable的修改操作（如 Add 和 Remove）不会就地修改现有集合，而是返回一个包含更改的新集合
            
            // Set: HashSet/SortedSet/ImmutableHashSet/FrozenSet
            var array = Enumerable.Range(0, 10).Select(x => x.ToString()).ToArray();
            var hashSet = array.ToHashSet();
            var immutableHashSet = array.ToImmutableHashSet();
            var frozenSet = array.ToFrozenSet();
            // 在只读的场景可以考虑使用 FrozenSet/FrozenDictionary Contains/NotContains方法查询性能最好
            
            // ImmutableHashSet/ImmutableDictionary
            
            var dictionary = new Dictionary<int, string>();
            
            // Dictionary: Dictionary/SortedDictionary/ImmutableDictionary/FrozenDictionary
        }
        
        public void ListDemo()
        {
            // List / LinkedList / SortedList
            //将一个数组中的奇数放到一个集合中，再将偶数放到另一个集合中
            //最终将两个集合合并为一个集合，并且奇数显示在左边 偶数显示在右边。

            int[] nums = [1, 2, 3, 4, 5, 6, 7, 8, 9];
            var listOu = new List<int>();
            var listJi = new List<int>();
            foreach (var t in nums)
            {
                if (t % 2 == 0)
                {
                    listOu.Add(t);
                }
                else
                {
                    listJi.Add(t);
                }
            }

            listOu.AddRange(listJi);
            foreach (var item in listOu)
            {
                Console.Write(item+"  ");
            }

            listJi.AddRange(listOu);
            foreach (var item in listJi)
            {
                Console.Write(item+"  ");
            }
            Console.ReadKey();
            var listSum = new List<int>();
            listSum.AddRange(listOu);
            listSum.AddRange(listJi);
        }

        public void DictionaryDemo()
        {
            //统计 Welcome to china中每个字符出现的次数 不考虑大小写

            var str = "Welcome to China";
            //字符 ------->出现的次数
            //键---------->值
            var dic = new Dictionary<char, int>();
            foreach (var t in str)
            {
                if (t == ' ')
                {
                    continue;
                }
                //如果dic已经包含了当前循环到的这个键 
                if (!dic.TryAdd(t, 1))
                {
                    //值再次加1
                    dic[t]++;
                }
                //这个字符在集合当中是第一次出现
            }

            foreach (var kv in dic)
            {
                Console.WriteLine("字母{0}出现了{1}次", kv.Key, kv.Value);
            }
            Console.ReadKey();
        }
        
        public void ListsDemo()
        {
            var people = new List<Person>
            {
                new Person { Name = "Alice", Age = 25 },
                new Person { Name = "Bob", Age = 30 },
                new Person { Name = "Charlie", Age = 35 }
            };

            // 使用LINQ查询从people列表中提取Name属性，创建一个新的字符串列表
            var names = people.Select(person => person.Name).ToList();

            // 输出提取的名字列表
            foreach (var name in names)
            {
                Console.WriteLine(name);
            }

            // 使用LINQ查询从people列表中提取Age属性，并计算总和
            var totalAge = people.Sum(p => p.Age);

            Console.WriteLine("Total age: " + totalAge);
        }

        public class Dto
        {
            public string Gender { get; set; }
            public string Name { get; set; }
        }
        
        // 拆分列表
        public void ListGroup()
        {
            var originalList = new List<Dto>
            {
                new() { Gender = "男", Name = "张三" },
                new() { Gender = "男", Name = "李四" },
                new() { Gender = "女", Name = "王二" },
            };

            // 一.按照索引
            // 方法一：使用LINQ的Skip和Take方法
            var splitIndex = originalList.Count / 2; // 拆分的索引位置
            var firstHalf1 = originalList.Take(splitIndex).ToList(); // 取前一半元素
            var secondHalf1 = originalList.Skip(splitIndex).ToList(); // 跳过前一半元素，取剩余元素
            
            var firstHalf2 = new List<Dto>(); // 存放前一半元素
            var secondHalf2 = new List<Dto>(); // 存放后一半元素
            for (var i = 0; i < originalList.Count; i++)
            {
                if (i < splitIndex)
                {
                    firstHalf2.Add(originalList[i]); // 添加到前一半列表
                }
                else
                {
                    secondHalf2.Add(originalList[i]); // 添加到后一半列表
                }
            }
            
            // 二.按条件
            // 方式一: foreach
            var male0 = new List<Dto>();
            var female0 = new List<Dto>();
            foreach (var dto in originalList)
            {
                if (dto.Gender == "男")
                {
                    male0.Add(dto);
                }
                else
                {
                    female0.Add(dto);
                }
            }
            
            // 方法2: where
            var males1 = originalList.Where(item => item.Gender == "男").ToList();
            var females1 = originalList.Where(item => item.Gender == "女").ToList();

            // 方法3: 使用LINQ按性别使用GroupBy分组到Dictionary中
            var groupedByGender = originalList.GroupBy(item => item.Gender)
                .ToDictionary(group => group.Key, group => group.ToList());
            var males2 = groupedByGender["男"];
            var females2 = groupedByGender["女"];
            
            // 方法4：使用LINQ的ToLookup方法，每个ID可以有多个学生关联
            var lookupByGender = originalList.ToLookup(item => item.Gender);
            var males3 = lookupByGender["Male"].ToList();
            var females3 = lookupByGender["Female"].ToList();
        }

        //去除List中的重复项
        public void ListDistinct()
        {
            var numbers = new List<int> { 1, 2, 2, 3, 4, 4, 5 };

            //方法1: 使用LINQ的Distinct()方法
            var uniqueNumbers = numbers.Distinct().ToList();

            //方法2: 使用HashSet
            var uniqueNums = new HashSet<int>(numbers);
            var resultList = new List<int>(uniqueNums);

            //方法3: Dictionary / List 使用循环和临时变量存储不重复的值
            var unique = new List<int>(); // 存储不重复的数字的列表
            foreach (var number in numbers)
            {
                if (!unique.Contains(number)) // 检查数字是否已经存在于列表中
                {
                    unique.Add(number); // 如果不存在，添加到列表中
                }
            }

            foreach (var number in uniqueNumbers)
            {
                Console.WriteLine(number);
            }
        }
        
        // List<Dto> 去重
        public void ListDtoRepeat()
        {
            var list = new List<Person>
            {
                new() { Id = 1, Name = "Alice" },
                new() { Id = 2, Name = "Bob" },
                new() { Id = 1, Name = "Alice" } // 重复项
            };

            // 方法1：使用LINQ的GroupBy
            var hasDuplicates1 = list.GroupBy(x => x.Name).Any(g => g.Count() > 1);
            Console.WriteLine(hasDuplicates1);
            
            // 方法2：使用HashSet来检测重复
            var names = list.Select(x => x.Name).ToList(); // 提取名字列表以便使用HashSet检测重复项
            var uniqueNames = new HashSet<string>(names); // HashSet会自动去除重复项
            var hasDuplicates2 = names.Count != uniqueNames.Count; // 比较总数和去重后的数量是否相等
            Console.WriteLine(hasDuplicates2); // 输出：True（如果有重复）
            
            // 方法3：使用Distinct结合Count检查数量差异
            var hasDuplicates3 = list.Count != list.Distinct().Count(); // 直接比较总数和去重后的数量是否相等
            Console.WriteLine(hasDuplicates3); // 输出：True（如果有重复）
            
            // 方法4：使用字典（Dictionary）统计出现次数（适用于找出所有重复项及其计数）
        }
        
        public void ListDtoDistinct()
        {
            var list = new List<Person>
            {
                new() { Id = 1, Name = "Alice" },
                new() { Id = 2, Name = "Bob" },
                new() { Id = 1, Name = "Alice" } // 重复项
            };
 
            // 方法1：使用 LINQ 的 Distinct 方法 注意: 需要类实现了IEquatable<T>接口或者在类中重写了Equals和GetHashCode方法，你可以使用LINQ的Distinct方法
            var distinctList1 = list.Distinct().ToList();
            foreach (var item in distinctList1)
            {
                Console.WriteLine($"Id: {item.Id}, Name: {item.Name}");
            }
            
            // 方法2：使用 GroupBy 后选择第一个元素
            var distinctList2 = list.GroupBy(x => new { x.Id, x.Name }) // 基于Id和Name分组
                .Select(g => g.First()) // 选择每个组的第一个元素（理论上应该是唯一的）
                .ToList(); // 转换为列表
            foreach (var item in distinctList2)
            {
                Console.WriteLine($"Id: {item.Id}, Name: {item.Name}");
            }
            
            // 方法3：使用 HashSet（不保持原始顺序）
            var hashSet = new HashSet<Person>(list); // 使用HashSet自动去重（基于Equals和GetHashCode）
            var distinctList = hashSet.ToList(); // 转换回List（如果需要）
            foreach (var item in distinctList) // 遍历去重后的列表。注意：顺序可能不同。
            {
                Console.WriteLine($"Id: {item.Id}, Name: {item.Name}");
            }
        }
    }
    
    public partial class Person : IEquatable<Person>
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
        // 还可以有其他属性...
        
        public bool Equals(Person? other)
        {
            if (other is null) return false;
            if (ReferenceEquals(this, other)) return true;
            return Name == other.Name;
        }
        
        public override int GetHashCode()
        {
            return HashCode.Combine(Name);
        }
    }
    
    // 去重 HashSet Linq的Distinct()
    
    
}
